import {
  ExceptionFilter,
  Catch,
  ArgumentsHost,
  HttpException,
  HttpStatus,
  Logger,
} from '@nestjs/common';
import { Request, Response } from 'express';

// 异常
import {
  ScoringException,
  FileValidationException,
  AiInferenceException,
  TaskNotFoundException,
  UnauthorizedAccessException,
  TaskStateException,
  QueueException,
  CacheException,
  DatabaseException,
  ExceptionUtils,
  ErrorCode,
} from '../exceptions/scoring.exceptions';

/**
 * Scoring模块异常过滤器
 * 零隐患MFIS规范 - 解决隐患：#14 日志信息泄露，#2 异常处理不当
 */
@Catch(
  ScoringException,
  FileValidationException,
  AiInferenceException,
  TaskNotFoundException,
  UnauthorizedAccessException,
  TaskStateException,
  QueueException,
  CacheException,
  DatabaseException,
  HttpException,
  Error,
)
export class ScoringExceptionFilter implements ExceptionFilter {
  private readonly logger = new Logger(ScoringExceptionFilter.name);

  catch(exception: unknown, host: ArgumentsHost): void {
    const ctx = host.switchToHttp();
    const response = ctx.getResponse<Response>();
    const request = ctx.getRequest<Request>();

    // 构建错误响应
    const errorResponse = this.buildErrorResponse(exception, request);

    // 记录异常日志（脱敏后）
    this.logException(exception, request, errorResponse);

    // 发送响应
    response.status(errorResponse.statusCode).json(errorResponse.body);
  }

  /**
   * 构建错误响应
   * 解决隐患：#14 日志信息泄露，响应脱敏
   */
  private buildErrorResponse(exception: unknown, request: Request): {
    statusCode: number;
    body: any;
  } {
    let statusCode = HttpStatus.INTERNAL_SERVER_ERROR;
    let errorCode = ErrorCode.UNKNOWN_ERROR;
    let message = '内部服务器错误';
    let details: any = undefined;

    // 处理自定义异常
    if (exception instanceof ScoringException) {
      const exceptionResponse = exception.getResponse() as any;
      statusCode = exception.getStatus();
      errorCode = exceptionResponse.error_code || ErrorCode.UNKNOWN_ERROR;
      message = exceptionResponse.message || '未知错误';
      details = exceptionResponse.details;
    }
    // 处理HTTP异常
    else if (exception instanceof HttpException) {
      statusCode = exception.getStatus();
      const exceptionResponse = exception.getResponse();

      if (typeof exceptionResponse === 'string') {
        message = exceptionResponse;
      } else if (typeof exceptionResponse === 'object') {
        const responseObj = exceptionResponse as any;
        message = responseObj.message || responseObj.error || 'HTTP错误';
        details = responseObj.details;
      }

      // 映射HTTP状态码到错误代码
      errorCode = this.mapHttpStatusToErrorCode(statusCode);
    }
    // 处理其他错误
    else if (exception instanceof Error) {
      // 隐患 #14 解决：脱敏错误消息
      message = this.sanitizeErrorMessage(exception.message);

      // 根据错误类型确定状态码
      statusCode = this.determineStatusCodeFromError(exception);
      errorCode = this.determineErrorCodeFromError(exception);
    }

    // 构建响应体
    const responseBody = {
      success: false,
      error_code: errorCode,
      message,
      details,
      timestamp: new Date().toISOString(),
      path: request.url,
      method: request.method,
      // 只在开发环境包含堆栈信息
      ...(process.env.NODE_ENV === 'development' && {
        stack: (exception as Error)?.stack,
      }),
    };

    return { statusCode, body: responseBody };
  }

  /**
   * 记录异常日志
   * 解决隐患：#14 日志信息泄露，#2 异常处理
   */
  private logException(exception: unknown, request: Request, errorResponse: any): void {
    const logData = {
      method: request.method,
      url: request.url,
      ip: request.ip,
      userAgent: request.get('User-Agent'),
      statusCode: errorResponse.statusCode,
      errorCode: errorResponse.body.error_code,
      message: errorResponse.body.message,
      timestamp: new Date().toISOString(),
    };

    // 添加用户信息（如果可用）
    if ((request as any).user) {
      logData['user'] = {
        doctor_id: (request as any).user.doctor_id,
        username: (request as any).user.username,
      };
    }

    // 添加请求ID（如果可用）
    const requestId = request.get('X-Request-ID');
    if (requestId) {
      logData['requestId'] = requestId;
    }

    // 根据异常类型选择日志级别
    if (exception instanceof ScoringException) {
      switch (exception.constructor.name) {
        case 'TaskNotFoundException':
          this.logger.warn('任务未找到', logData);
          break;
        case 'UnauthorizedAccessException':
          this.logger.warn('未授权访问', logData);
          break;
        case 'FileValidationException':
          this.logger.warn('文件验证失败', logData);
          break;
        case 'AiInferenceException':
          this.logger.error('AI推理异常', logData);
          break;
        default:
          this.logger.error('Scoring模块异常', logData);
      }
    } else if (exception instanceof HttpException) {
      if (exception.getStatus() >= 500) {
        this.logger.error('HTTP服务器错误', logData);
      } else {
        this.logger.warn('HTTP客户端错误', logData);
      }
    } else {
      this.logger.error('未知异常', {
        ...logData,
        exceptionName: (exception as Error)?.constructor.name,
        // 只在开发环境记录完整错误
        ...(process.env.NODE_ENV === 'development' && {
          exception: exception,
        }),
      });
    }

    // 安全敏感异常的特殊处理
    if (this.isSecuritySensitiveException(exception)) {
      this.logger.warn('检测到安全敏感异常', {
        ...logData,
        securityFlag: true,
        requiresAttention: true,
      });
    }
  }

  /**
   * 映射HTTP状态码到错误代码
   */
  private mapHttpStatusToErrorCode(statusCode: number): string {
    const statusMap = {
      [HttpStatus.BAD_REQUEST]: ErrorCode.VALIDATION_FAILED,
      [HttpStatus.UNAUTHORIZED]: ErrorCode.UNAUTHORIZED_ACCESS,
      [HttpStatus.FORBIDDEN]: ErrorCode.FORBIDDEN_OPERATION,
      [HttpStatus.NOT_FOUND]: ErrorCode.TASK_NOT_FOUND,
      [HttpStatus.CONFLICT]: ErrorCode.INVALID_TASK_STATE,
      [HttpStatus.TOO_MANY_REQUESTS]: ErrorCode.TIMEOUT_ERROR,
      [HttpStatus.INTERNAL_SERVER_ERROR]: ErrorCode.UNKNOWN_ERROR,
      [HttpStatus.BAD_GATEWAY]: ErrorCode.AI_SERVICE_UNAVAILABLE,
      [HttpStatus.SERVICE_UNAVAILABLE]: ErrorCode.AI_SERVICE_UNAVAILABLE,
      [HttpStatus.GATEWAY_TIMEOUT]: ErrorCode.TIMEOUT_ERROR,
    };

    return statusMap[statusCode] || ErrorCode.UNKNOWN_ERROR;
  }

  /**
   * 根据错误类型确定状态码
   */
  private determineStatusCodeFromError(error: Error): number {
    // 数据库连接错误
    if (error.message.includes('ECONNREFUSED') || error.message.includes('connect')) {
      return HttpStatus.SERVICE_UNAVAILABLE;
    }

    // 文件系统错误
    if (error.message.includes('ENOENT') || error.message.includes('file not found')) {
      return HttpStatus.BAD_REQUEST;
    }

    // 权限错误
    if (error.message.includes('permission') || error.message.includes('EACCES')) {
      return HttpStatus.FORBIDDEN;
    }

    // 网络超时
    if (error.message.includes('timeout') || error.message.includes('ETIMEDOUT')) {
      return HttpStatus.GATEWAY_TIMEOUT;
    }

    return HttpStatus.INTERNAL_SERVER_ERROR;
  }

  /**
   * 根据错误类型确定错误代码
   */
  private determineErrorCodeFromError(error: Error): string {
    if (error.message.includes('database') || error.message.includes('prisma')) {
      return ErrorCode.DATABASE_ERROR;
    }

    if (error.message.includes('queue') || error.message.includes('bullmq')) {
      return ErrorCode.QUEUE_ERROR;
    }

    if (error.message.includes('cache') || error.message.includes('redis')) {
      return ErrorCode.CACHE_ERROR;
    }

    if (error.message.includes('network') || error.message.includes('ECONN')) {
      return ErrorCode.NETWORK_ERROR;
    }

    if (error.message.includes('timeout')) {
      return ErrorCode.TIMEOUT_ERROR;
    }

    return ErrorCode.UNKNOWN_ERROR;
  }

  /**
   * 判断是否为安全敏感异常
   */
  private isSecuritySensitiveException(exception: unknown): boolean {
    const securityKeywords = [
      'injection',
      'xss',
      'csrf',
      'unauthorized',
      'forbidden',
      'authentication',
      'token',
      'password',
      'brute force',
      'rate limit',
    ];

    const exceptionMessage = (exception as Error)?.message?.toLowerCase() || '';
    const exceptionName = (exception as Error)?.constructor?.name?.toLowerCase() || '';

    return securityKeywords.some(keyword =>
      exceptionMessage.includes(keyword) || exceptionName.includes(keyword)
    );
  }

  /**
   * 错误消息脱敏
   * 解决隐患：#14 日志信息泄露
   */
  private sanitizeErrorMessage(message: string): string {
    if (!message) return '未知错误';

    return message
      // 移除文件路径
      .replace(/[A-Za-z]:\\[^\\s]*/g, '[文件路径]')
      .replace(/\/[^\\s]*\/[^\\s]*/g, '[文件路径]')

      // 移除敏感信息
      .replace(/password[=:]\s*\S+/gi, 'password=***')
      .replace(/token[=:]\s*\S+/gi, 'token=***')
      .replace(/key[=:]\s*\S+/gi, 'key=***')
      .replace(/secret[=:]\s*\S+/gi, 'secret=***')
      .replace(/auth[=:]\s*\S+/gi, 'auth=***')

      // 移除数据库连接信息
      .replace(/mysql:\/\/[^@]+@[^\/]+/g, 'mysql://***@***/')
      .replace(/redis:\/\/[^@]+@[^\/]+/g, 'redis://***@***/')

      // 移除IP地址
      .replace(/\b(?:[0-9]{1,3}\.){3}[0-9]{1,3}\b/g, '[IP地址]')

      // 移除可能的SQL注入内容
      .replace(/(select|insert|update|delete|drop|union|exec|script)/gi, '[SQL关键词]')

      // 限制长度
      .substring(0, 200)
      .trim() || '错误信息已被过滤';
  }
}